home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2008 February / PCWFEB08.iso / Software / Freeware / Miro 1.0 / Miro_Installer.exe / xulrunner / components / nsPostUpdateWin.js < prev    next >
Encoding:
Text File  |  2007-11-12  |  23.4 KB  |  837 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Mozilla Public License Version
  6.  * 1.1 (the "License"); you may not use this file except in compliance with
  7.  * the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/MPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is the Update Service.
  16.  *
  17.  * The Initial Developer of the Original Code is Google Inc.
  18.  * Portions created by the Initial Developer are Copyright (C) 2005
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *  Darin Fisher <darin@meer.net> (original author)
  23.  *
  24.  * Alternatively, the contents of this file may be used under the terms of
  25.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  26.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  27.  * in which case the provisions of the GPL or the LGPL are applicable instead
  28.  * of those above. If you wish to allow use of your version of this file only
  29.  * under the terms of either the GPL or the LGPL, and not to allow others to
  30.  * use your version of this file under the terms of the MPL, indicate your
  31.  * decision by deleting the provisions above and replace them with the notice
  32.  * and other provisions required by the GPL or the LGPL. If you do not delete
  33.  * the provisions above, a recipient may use your version of this file under
  34.  * the terms of any one of the MPL, the GPL or the LGPL.
  35.  *
  36.  * ***** END LICENSE BLOCK ***** */
  37.  
  38. /**
  39.  * This file contains an implementation of nsIRunnable, which may be invoked
  40.  * to perform post-update modifications to the windows registry and uninstall
  41.  * logs required to complete an update of the application.  This code is very
  42.  * specific to the xpinstall wizard for windows.
  43.  */
  44.  
  45. const URI_BRAND_PROPERTIES     = "chrome://branding/locale/brand.properties";
  46. const URI_UNINSTALL_PROPERTIES = "chrome://branding/content/uninstall.properties";
  47.  
  48. const KEY_APPDIR          = "XCurProcD";
  49. const KEY_COMPONENTS_DIR  = "ComsD";
  50. const KEY_PLUGINS_DIR     = "APlugns";
  51. const KEY_EXECUTABLE_FILE = "XREExeF";
  52.  
  53. // see prio.h
  54. const PR_RDONLY      = 0x01;
  55. const PR_WRONLY      = 0x02;
  56. const PR_APPEND      = 0x10;
  57.  
  58. const nsIWindowsRegKey = Components.interfaces.nsIWindowsRegKey;
  59.  
  60. //-----------------------------------------------------------------------------
  61.  
  62. /**
  63.  * Console logging support
  64.  */
  65. function LOG(s) {
  66.   dump("*** PostUpdateWin: " + s + "\n");
  67. }
  68.  
  69. /**
  70.  * This function queries the XPCOM directory service.
  71.  */
  72. function getFile(key) {
  73.   var dirSvc =
  74.       Components.classes["@mozilla.org/file/directory_service;1"].
  75.       getService(Components.interfaces.nsIProperties);
  76.   return dirSvc.get(key, Components.interfaces.nsIFile);
  77. }
  78.  
  79. /**
  80.  * Return the full path given a relative path and a base directory.
  81.  */
  82. function getFileRelativeTo(dir, relPath) {
  83.   var file = dir.clone().QueryInterface(Components.interfaces.nsILocalFile);
  84.   file.setRelativeDescriptor(dir, relPath);
  85.   return file;
  86. }
  87.  
  88. /**
  89.  * Creates a new file object given a native file path.
  90.  * @param   path
  91.  *          The native file path.
  92.  * @return  nsILocalFile object for the given native file path.
  93.  */
  94. function newFile(path) {
  95.   var file = Components.classes["@mozilla.org/file/local;1"]
  96.                        .createInstance(Components.interfaces.nsILocalFile);
  97.   file.initWithPath(path);
  98.   return file;
  99. }
  100.  
  101. /**
  102.  * This function returns a file input stream.
  103.  */
  104. function openFileInputStream(file) {
  105.   var stream =
  106.       Components.classes["@mozilla.org/network/file-input-stream;1"].
  107.       createInstance(Components.interfaces.nsIFileInputStream);
  108.   stream.init(file, PR_RDONLY, 0, 0);
  109.   return stream;
  110. }
  111.  
  112. /**
  113.  * This function returns a file output stream.
  114.  */
  115. function openFileOutputStream(file, flags) {
  116.   var stream =
  117.       Components.classes["@mozilla.org/network/file-output-stream;1"].
  118.       createInstance(Components.interfaces.nsIFileOutputStream);
  119.   stream.init(file, flags, 0644, 0);
  120.   return stream;
  121. }
  122.  
  123. /**
  124.  * Gets the current value of the locale.  It's possible for this preference to
  125.  * be localized, so we have to do a little extra work here.  Similar code
  126.  * exists in nsHttpHandler.cpp when building the UA string.
  127.  */
  128. function getLocale() {
  129.   const prefName = "general.useragent.locale";
  130.   var prefs =
  131.       Components.classes["@mozilla.org/preferences-service;1"].
  132.       getService(Components.interfaces.nsIPrefBranch);
  133.   try {
  134.     return prefs.getComplexValue(prefName,
  135.                Components.interfaces.nsIPrefLocalizedString).data;
  136.   } catch (e) {}
  137.  
  138.   return prefs.getCharPref(prefName);
  139. }
  140.  
  141. //-----------------------------------------------------------------------------
  142.  
  143. const PREFIX_INSTALLING = "installing: ";
  144. const PREFIX_CREATE_FOLDER = "create folder: ";
  145. const PREFIX_CREATE_REGISTRY_KEY = "create registry key: ";
  146. const PREFIX_STORE_REGISTRY_VALUE_STRING = "store registry value string: ";
  147.  
  148. function InstallLogWriter() {
  149. }
  150. InstallLogWriter.prototype = {
  151.   _outputStream: null,  // nsIOutputStream to the install wizard log file
  152.  
  153.   /**
  154.    * Write a single line to the output stream.
  155.    */
  156.   _writeLine: function(s) {
  157.     s = s + "\n";
  158.     this._outputStream.write(s, s.length);
  159.   },
  160.  
  161.   /**
  162.    * This function creates an empty install wizard log file and returns
  163.    * a reference to the resulting nsIFile.
  164.    */
  165.   _getInstallLogFile: function() {
  166.     function makeLeafName(index) {
  167.       return "install_wizard" + index + ".log"
  168.     }
  169.  
  170.     var file = getFile(KEY_APPDIR); 
  171.     file.append("uninstall");
  172.     if (!file.exists())
  173.       return null;
  174.  
  175.     file.append("x");
  176.     var n = 0;
  177.     do {
  178.       file.leafName = makeLeafName(++n);
  179.     } while (file.exists());
  180.  
  181.     if (n == 1)
  182.       return null;  // What, no install wizard log file?
  183.  
  184.     file.leafName = makeLeafName(n - 1);
  185.     return file;
  186.   },
  187.  
  188.   /**
  189.    * Return the update.log file.  Use last-update.log file in case the
  190.    * updates/0 directory has already been cleaned out (see bug 311302).
  191.    */
  192.   _getUpdateLogFile: function() {
  193.     var file = getFile(KEY_APPDIR); 
  194.     file.append("updates");
  195.     file.append("0");
  196.     file.append("update.log");
  197.     if (file.exists())
  198.       return file;
  199.  
  200.     file = getFile(KEY_APPDIR); 
  201.     file.append("updates");
  202.     file.append("last-update.log");
  203.     if (file.exists())
  204.       return file;
  205.  
  206.     return null;
  207.   },
  208.  
  209.   /**
  210.    * Read update.log to extract information about files that were
  211.    * newly added for this update.
  212.    */
  213.   _readUpdateLog: function(logFile, entries) {
  214.     var appDir = getFile(KEY_APPDIR);
  215.     var stream;
  216.     try {
  217.       stream = openFileInputStream(logFile).
  218.           QueryInterface(Components.interfaces.nsILineInputStream);
  219.  
  220.       var dirs = {};
  221.       var line = {};
  222.       while (stream.readLine(line)) {
  223.         var data = line.value.split(" ");
  224.         if (data[0] == "EXECUTE" && data[1] == "ADD") {
  225.           var file = getFileRelativeTo(appDir, data[2]);
  226.           
  227.           // remember all parent directories
  228.           var parent = file.parent;
  229.           while (!parent.equals(appDir)) {
  230.             dirs[parent.path] = true;
  231.             parent = parent.parent;
  232.           }
  233.  
  234.           // remember the file
  235.           entries.files.push(file.path);
  236.         }
  237.       }
  238.       for (var d in dirs)
  239.         entries.dirs.push(d);
  240.       // Sort the directories so that subdirectories are deleted first.
  241.       entries.dirs.sort();
  242.     } finally {
  243.       if (stream)
  244.         stream.close();
  245.     }
  246.   },
  247.  
  248.   /**
  249.    * This function initializes the log writer and is responsible for
  250.    * translating 'update.log' to the install wizard format.
  251.    */
  252.   begin: function() {
  253.     var installLog = this._getInstallLogFile();
  254.     var updateLog = this._getUpdateLogFile();
  255.     if (!installLog || !updateLog)
  256.       return;
  257.  
  258.     var entries = { dirs: [], files: [] };
  259.     this._readUpdateLog(updateLog, entries);
  260.  
  261.     this._outputStream =
  262.         openFileOutputStream(installLog, PR_WRONLY | PR_APPEND);
  263.     this._writeLine("\nUPDATE [" + new Date().toUTCString() + "]");
  264.  
  265.     var i;
  266.     // The log file is processed in reverse order, so list directories before
  267.     // files to ensure that the directories will be deleted.
  268.     for (i = 0; i < entries.dirs.length; ++i)
  269.       this._writeLine(PREFIX_CREATE_FOLDER + entries.dirs[i]);
  270.     for (i = 0; i < entries.files.length; ++i)
  271.       this._writeLine(PREFIX_INSTALLING + entries.files[i]);
  272.   },
  273.  
  274.   /**
  275.    * This function records the creation of a registry key.
  276.    */
  277.   registryKeyCreated: function(keyPath) {
  278.     if (!this._outputStream)
  279.       return;
  280.     this._writeLine(PREFIX_CREATE_REGISTRY_KEY + keyPath + " []");
  281.   },
  282.  
  283.   /**
  284.    * This function records the creation of a registry key value.
  285.    */
  286.   registryKeyValueSet: function(keyPath, valueName) {
  287.     if (!this._outputStream)
  288.       return;
  289.     this._writeLine(
  290.         PREFIX_STORE_REGISTRY_VALUE_STRING + keyPath + " [" + valueName + "]");
  291.   },
  292.  
  293.   end: function() {
  294.     if (!this._outputStream)
  295.       return;
  296.     this._outputStream.close();
  297.     this._outputStream = null;
  298.   }
  299. };
  300.  
  301. var installLogWriter;
  302.  
  303. //-----------------------------------------------------------------------------
  304.  
  305. /**
  306.  * A thin wrapper around nsIWindowsRegKey that keeps track of its path
  307.  * and notifies the installLogWriter when modifications are made.
  308.  */
  309. function RegKey() {
  310.   // Internally, we may pass parameters to this constructor.
  311.   if (arguments.length == 3) {
  312.     this._key = arguments[0];
  313.     this._root = arguments[1];
  314.     this._path = arguments[2];
  315.   } else {
  316.     this._key =
  317.         Components.classes["@mozilla.org/windows-registry-key;1"].
  318.         createInstance(nsIWindowsRegKey);
  319.   }
  320. }
  321. RegKey.prototype = {
  322.   _key: null,
  323.   _root: null,
  324.   _path: null,
  325.  
  326.   ACCESS_READ:  nsIWindowsRegKey.ACCESS_READ,
  327.   ACCESS_WRITE: nsIWindowsRegKey.ACCESS_WRITE,
  328.   ACCESS_ALL:   nsIWindowsRegKey.ACCESS_ALL,
  329.  
  330.   ROOT_KEY_CURRENT_USER: nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
  331.   ROOT_KEY_LOCAL_MACHINE: nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
  332.  
  333.   close: function() {
  334.     this._key.close();
  335.     this._root = null;
  336.     this._path = null;
  337.   },
  338.  
  339.   open: function(rootKey, path, mode) {
  340.     this._key.open(rootKey, path, mode);
  341.     this._root = rootKey;
  342.     this._path = path;
  343.   },
  344.  
  345.   openChild: function(path, mode) {
  346.     var child = this._key.openChild(path, mode);
  347.     return new RegKey(child, this._root, this._path + "\\" + path);
  348.   },
  349.  
  350.   createChild: function(path, mode) {
  351.     var child = this._key.createChild(path, mode);
  352.     var key = new RegKey(child, this._root, this._path + "\\" + path);
  353.     if (installLogWriter)
  354.       installLogWriter.registryKeyCreated(key.toString());
  355.     return key;
  356.   },
  357.  
  358.   readStringValue: function(name) {
  359.     return this._key.readStringValue(name);
  360.   },
  361.  
  362.   writeStringValue: function(name, value) {
  363.     this._key.writeStringValue(name, value);
  364.     if (installLogWriter)
  365.       installLogWriter.registryKeyValueSet(this.toString(), name);
  366.   },
  367.  
  368.   writeIntValue: function(name, value) {
  369.     this._key.writeIntValue(name, value);
  370.     if (installLogWriter)
  371.       installLogWriter.registryKeyValueSet(this.toString(), name);
  372.   },
  373.  
  374.   hasValue: function(name) {
  375.     return this._key.hasValue(name);
  376.   },
  377.  
  378.   hasChild: function(name) {
  379.     return this._key.hasChild(name);
  380.   },
  381.  
  382.   get childCount() {
  383.     return this._key.childCount;
  384.   },
  385.  
  386.   getChildName: function(index) {
  387.     return this._key.getChildName(index);
  388.   },
  389.  
  390.   removeChild: function(name) {
  391.     this._key.removeChild(name);
  392.   },
  393.  
  394.   toString: function() {
  395.     var root;
  396.     switch (this._root) {
  397.     case this.ROOT_KEY_LOCAL_MACHINE:
  398.       root = "HKEY_LOCAL_MACHINE";
  399.       break;
  400.     case this.ROOT_KEY_CURRENT_USER:
  401.       root = "HKEY_CURRENT_USER";
  402.       break;
  403.     default:
  404.       LOG("unknown root key");
  405.       return "";
  406.     }
  407.     return root + "\\" + this._path;
  408.   }
  409. };
  410.  
  411. //-----------------------------------------------------------------------------
  412.  
  413. /*
  414. RegKey.prototype = {
  415.   // The name of the registry key
  416.   name: "";
  417.  
  418.   // An array of strings, where each even-indexed string names a value,
  419.   // and the subsequent string provides data for the value.
  420.   values: [];
  421.  
  422.   // An array of RegKey objects.
  423.   children: [];
  424. }
  425. */
  426.  
  427. /**
  428.  * This function creates a heirarchy of registry keys.  If any of the
  429.  * keys or values already exist, then they will be updated instead.
  430.  * @param rootKey
  431.  *        The root registry key from which to create the new registry
  432.  *        keys.
  433.  * @param data
  434.  *        A JS object with properties: "name", "values", and "children"
  435.  *        as defined above.  All children of this key will be created.
  436.  */
  437. function createRegistryKeys(rootKey, data) {
  438.   var key;
  439.   try {
  440.     key = rootKey.createChild(data.name, rootKey.ACCESS_WRITE);
  441.     var i;
  442.     if ("values" in data) {
  443.       for (i = 0; i < data.values.length; i += 2)
  444.         key.writeStringValue(data.values[i], data.values[i + 1]); 
  445.     }
  446.     if ("children" in data) {
  447.       for (i = 0; i < data.children.length; ++i)
  448.         createRegistryKeys(key, data.children[i]);
  449.     }
  450.     key.close();
  451.   } catch (e) {
  452.     LOG(e);
  453.     if (key)
  454.       key.close();
  455.   }
  456. }
  457.  
  458. /**
  459.  * This function deletes the specified registry key and optionally all of its
  460.  * children.
  461.  * @param rootKey
  462.  *        The parent nsIwindowRegKey of the key to delete.
  463.  * @param name
  464.  *        The name of the key to delete.
  465.  * @param recurse
  466.  *        Pass true to also delete all children of the named key.  Take care!
  467.  */
  468. function deleteRegistryKey(rootKey, name, recurse) {
  469.   if (!rootKey.hasChild(name)) {
  470.     LOG("deleteRegistryKey: rootKey does not have child: \"" + name + "\"");
  471.     return;
  472.   }
  473.   if (recurse) {
  474.     var key = rootKey.openChild(name, rootKey.ACCESS_ALL);
  475.     try {
  476.       for (var i = key.childCount - 1; i >= 0; --i)
  477.         deleteRegistryKey(key, key.getChildName(i), true);
  478.     } finally {
  479.       key.close();
  480.     }
  481.   }
  482.   rootKey.removeChild(name);
  483. }
  484.  
  485. /**
  486.  * This method walks the registry looking for the registry keys of
  487.  * the previous version of the application.
  488.  */
  489. function locateOldInstall(key, ourInstallDir) {
  490.   var result, childKey, productKey, mainKey;
  491.   try {
  492.     for (var i = 0; i < key.childCount; ++i) {
  493.       var childName = key.getChildName(i);
  494.       childKey = key.openChild(childName, key.ACCESS_READ);
  495.       if (childKey.hasValue("CurrentVersion")) {
  496.         for (var j = 0; j < childKey.childCount; ++j) {
  497.           var productVer = childKey.getChildName(j); 
  498.           productKey = childKey.openChild(productVer, key.ACCESS_READ);
  499.           if (productKey.hasChild("Main")) {
  500.             mainKey = productKey.openChild("Main", key.ACCESS_READ);
  501.             var installDir = mainKey.readStringValue("Install Directory");
  502.             var menuPath = mainKey.readStringValue("Program Folder Path");
  503.             mainKey.close();
  504.             if (newFile(installDir).equals(ourInstallDir)) {
  505.               result = new Object();
  506.               result.fullName = childName;
  507.               result.versionWithLocale = productVer;
  508.               result.version = productVer.split(" ")[0];
  509.               result.menuPath = menuPath;
  510.             }
  511.           }
  512.           productKey.close();
  513.           if (result)
  514.             break;
  515.         }
  516.       }
  517.       childKey.close();
  518.       if (result)
  519.         break;
  520.     }
  521.   } catch (e) {
  522.     result = null;
  523.     if (childKey)
  524.       childKey.close();
  525.     if (productKey)
  526.       productKey.close();
  527.     if (mainKey)
  528.       mainKey.close();
  529.   }
  530.   return result;
  531. }
  532.  
  533. /**
  534.  * Delete registry keys left-over from the previous version of the app
  535.  * installed at our location.
  536.  */
  537. function deleteOldRegKeys(key, info) {
  538.   deleteRegistryKey(key, info.fullName + " " + info.version, true);
  539.   var productKey = key.openChild(info.fullName, key.ACCESS_ALL);
  540.   var productCount;
  541.   try {
  542.     deleteRegistryKey(productKey, info.versionWithLocale, true);
  543.     productCount = productKey.childCount;
  544.   } finally {
  545.     productKey.close();
  546.   }
  547.   if (productCount == 0)
  548.     key.removeChild(info.fullName);  
  549. }
  550.  
  551. /**
  552.  * The installer sets various registry keys and values that may need to be
  553.  * updated.
  554.  *
  555.  * This operation is a bit tricky since we do not know the previous value of
  556.  * brandFullName.  As a result, we must walk the registry looking for an
  557.  * existing key that references the same install directory.  We assume that
  558.  * the value of vendorShortName does not change across updates.
  559.  */
  560. function updateRegistry(rootKey) {
  561.   LOG("updateRegistry");
  562.  
  563.   var ourInstallDir = getFile(KEY_APPDIR);
  564.  
  565.   var app =
  566.     Components.classes["@mozilla.org/xre/app-info;1"].
  567.     getService(Components.interfaces.nsIXULAppInfo).
  568.     QueryInterface(Components.interfaces.nsIXULRuntime);
  569.  
  570.   var sbs =
  571.       Components.classes["@mozilla.org/intl/stringbundle;1"].
  572.       getService(Components.interfaces.nsIStringBundleService);
  573.   var brandBundle = sbs.createBundle(URI_BRAND_PROPERTIES);
  574.   var brandFullName = brandBundle.GetStringFromName("brandFullName");
  575.  
  576.   var vendorShortName;
  577.   try {
  578.     // The Thunderbird vendorShortName is "Mozilla Thunderbird", but we
  579.     // just want "Thunderbird", so allow it to be overridden in prefs.
  580.  
  581.     var prefs =
  582.       Components.classes["@mozilla.org/preferences-service;1"].
  583.       getService(Components.interfaces.nsIPrefBranch);
  584.  
  585.     vendorShortName = prefs.getCharPref("app.update.vendorName.override");
  586.   }
  587.   catch (e) {
  588.     vendorShortName = brandBundle.GetStringFromName("vendorShortName");
  589.   }
  590.  
  591.   var key = new RegKey();
  592.  
  593.   var oldInstall;
  594.   try {
  595.     key.open(rootKey, "SOFTWARE\\" + vendorShortName, key.ACCESS_READ);
  596.     oldInstall = locateOldInstall(key, ourInstallDir);
  597.   } finally {
  598.     key.close();
  599.   }
  600.  
  601.   if (!oldInstall) {
  602.     LOG("no existing registry keys found");
  603.     return;
  604.   }
  605.  
  606.   // Maybe nothing needs to be changed...
  607.   if (oldInstall.fullName == brandFullName &&
  608.       oldInstall.version == app.version) {
  609.     LOG("registry is up-to-date");
  610.     return;
  611.   }
  612.  
  613.   // Delete the old keys:
  614.   try {
  615.     key.open(rootKey, "SOFTWARE\\" + vendorShortName, key.ACCESS_READ);
  616.     deleteOldRegKeys(key, oldInstall);
  617.   } finally {
  618.     key.close();
  619.   }
  620.  
  621.   // Create the new keys:
  622.  
  623.   var versionWithLocale = app.version + " (" + getLocale() + ")";
  624.   var installPath = ourInstallDir.path + "\\";
  625.   var pathToExe = getFile(KEY_EXECUTABLE_FILE).path;
  626.  
  627.   var Key_bin = {
  628.     name: "bin",
  629.     values: [
  630.       "PathToExe", pathToExe
  631.     ]
  632.   };
  633.   var Key_extensions = {
  634.     name: "Extensions",
  635.     values: [
  636.       "Components", getFile(KEY_COMPONENTS_DIR).path + "\\",
  637.       "Plugins", getFile(KEY_PLUGINS_DIR).path + "\\"
  638.     ]
  639.   };
  640.   var Key_nameWithVersion = {
  641.     name: brandFullName + " " + app.version,
  642.     values: [
  643.       "GeckoVer", app.platformVersion
  644.     ],
  645.     children: [
  646.       Key_bin,
  647.       Key_extensions
  648.     ]
  649.   };
  650.   var Key_main = {
  651.     name: "Main",
  652.     values: [
  653.       "Install Directory", installPath,
  654.       "PathToExe", pathToExe,
  655.       "Program Folder Path", oldInstall.menuPath
  656.     ]
  657.   };
  658.   var Key_uninstall = {
  659.     name: "Uninstall",
  660.     values: [
  661.       "Description", brandFullName + " (" + app.version + ")",
  662.       "Uninstall Log Folder", installPath + "uninstall"
  663.     ]
  664.   };
  665.   var Key_versionWithLocale = {
  666.     name: versionWithLocale,
  667.     children: [
  668.       Key_main,
  669.       Key_uninstall
  670.     ]
  671.   };
  672.   var Key_name = {
  673.     name: brandFullName,
  674.     values: [
  675.       "CurrentVersion", versionWithLocale
  676.     ],
  677.     children: [
  678.       Key_versionWithLocale
  679.     ]
  680.   };
  681.   var Key_brand = {
  682.     name: vendorShortName,
  683.     children: [
  684.       Key_name,
  685.       Key_nameWithVersion
  686.     ]
  687.   };
  688.  
  689.   try {
  690.     key.open(rootKey, "SOFTWARE", key.ACCESS_READ);
  691.     createRegistryKeys(key, Key_brand);
  692.   } finally {
  693.     key.close();
  694.   }
  695.  
  696.   if (rootKey != RegKey.prototype.ROOT_KEY_LOCAL_MACHINE)
  697.     return;
  698.  
  699.   // Now, do the same thing for the Add/Remove Programs control panel:
  700.  
  701.   const uninstallRoot =
  702.       "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
  703.  
  704.   try {
  705.     key.open(rootKey, uninstallRoot, key.ACCESS_READ);
  706.     var oldName = oldInstall.fullName + " (" + oldInstall.version + ")";
  707.     deleteRegistryKey(key, oldName, false);
  708.   } finally {
  709.     key.close();
  710.   }
  711.  
  712.   var uninstallBundle = sbs.createBundle(URI_UNINSTALL_PROPERTIES);
  713.  
  714.   var nameWithVersion = brandFullName + " (" + app.version + ")";
  715.  
  716.   var uninstaller = getFile(KEY_APPDIR);
  717.   uninstaller.append("uninstall");
  718.   uninstaller.append("uninstall.exe");
  719.  
  720.   var uninstallString =
  721.       uninstaller.path + " /ua \"" + versionWithLocale + "\"";
  722.  
  723.   Key_uninstall = {
  724.     name: nameWithVersion,
  725.     values: [
  726.       "Comment", brandFullName,
  727.       "DisplayIcon", pathToExe + ",0",        // XXX don't hardcode me!
  728.       "DisplayName", nameWithVersion,
  729.       "DisplayVersion", versionWithLocale, 
  730.       "InstallLocation", ourInstallDir.path,  // no trailing slash
  731.       "Publisher", vendorShortName,
  732.       "UninstallString", uninstallString,
  733.       "URLInfoAbout", uninstallBundle.GetStringFromName("URLInfoAbout"),
  734.       "URLUpdateInfo", uninstallBundle.GetStringFromName("URLUpdateInfo") 
  735.     ]
  736.   };
  737.  
  738.   var child;
  739.   try {
  740.     key.open(rootKey, uninstallRoot, key.ACCESS_READ);
  741.     createRegistryKeys(key, Key_uninstall);
  742.  
  743.     // Create additional DWORD keys for NoModify and NoRepair:
  744.     child = key.openChild(nameWithVersion, key.ACCESS_WRITE);
  745.     child.writeIntValue("NoModify", 1);
  746.     child.writeIntValue("NoRepair", 1);
  747.   } finally {
  748.     if (child)
  749.       child.close();
  750.     key.close();
  751.   }
  752. }
  753.  
  754. //-----------------------------------------------------------------------------
  755.  
  756. function nsPostUpdateWin() {
  757. }
  758.  
  759. nsPostUpdateWin.prototype = {
  760.   QueryInterface: function(iid) {
  761.     if (iid.equals(Components.interfaces.nsIRunnable) ||
  762.         iid.equals(Components.interfaces.nsISupports))
  763.       return this;
  764.     throw Components.results.NS_ERROR_NO_INTERFACE;
  765.   },
  766.  
  767.   run: function() {
  768.     try {
  769.       // We use a global object here for the install log writer, so that the
  770.       // registry updating code can easily inform it of registry keys that it
  771.       // may create.
  772.       installLogWriter = new InstallLogWriter();
  773.       try {
  774.         installLogWriter.begin();
  775.         updateRegistry(RegKey.prototype.ROOT_KEY_CURRENT_USER);
  776.         updateRegistry(RegKey.prototype.ROOT_KEY_LOCAL_MACHINE);
  777.       } finally {
  778.         installLogWriter.end();
  779.         installLogWriter = null;
  780.       }
  781.     } catch (e) {
  782.       LOG(e);
  783.     }
  784.   }
  785. };
  786.  
  787. //-----------------------------------------------------------------------------
  788.  
  789. var gModule = {
  790.   registerSelf: function(compMgr, fileSpec, location, type) {
  791.     compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  792.     
  793.     for (var key in this._objects) {
  794.       var obj = this._objects[key];
  795.       compMgr.registerFactoryLocation(obj.CID, obj.className, obj.contractID,
  796.                                       fileSpec, location, type);
  797.     }
  798.   },
  799.   
  800.   getClassObject: function(compMgr, cid, iid) {
  801.     if (!iid.equals(Components.interfaces.nsIFactory))
  802.       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  803.  
  804.     for (var key in this._objects) {
  805.       if (cid.equals(this._objects[key].CID))
  806.         return this._objects[key].factory;
  807.     }
  808.     
  809.     throw Components.results.NS_ERROR_NO_INTERFACE;
  810.   },
  811.   
  812.   _makeFactory: #1= function(ctor) {
  813.     function ci(outer, iid) {
  814.       if (outer != null)
  815.         throw Components.results.NS_ERROR_NO_AGGREGATION;
  816.       return (new ctor()).QueryInterface(iid);
  817.     } 
  818.     return { createInstance: ci };
  819.   },
  820.   
  821.   _objects: {
  822.     manager: { CID        : Components.ID("{d15b970b-5472-40df-97e8-eb03a04baa82}"),
  823.                contractID : "@mozilla.org/updates/post-update;1",
  824.                className  : "nsPostUpdateWin",
  825.                factory    : #1#(nsPostUpdateWin)
  826.              },
  827.   },
  828.   
  829.   canUnload: function(compMgr) {
  830.     return true;
  831.   }
  832. };
  833.  
  834. function NSGetModule(compMgr, fileSpec) {
  835.   return gModule;
  836. }
  837.